﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Free.iso8583;
using Free.iso8583.hpdhEdc.model;
using Free.iso8583.model;

namespace SmartPay.Bussiness
{
    public class Iso8583
    {
        private Dictionary<Iso8583Operations, BaseModel> _models;
        private List<Exception> _errors;
        private MessageProcessor _processor;

        public Iso8583(string configPath)
        {
            DebugInfo = new StringBuilder();
            _errors = new List<Exception>();
            InitConfig(configPath);
            InitModels();
        }

        public void DoTransferInquiry()
        {
            Send(_models[Iso8583Operations.TransferInquiry]);
        }

        public void DoTransfer()
        {
            Send(_models[Iso8583Operations.Transfer]);
        }

        public void DoBalanceInquiry()
        {
            Send(_models[Iso8583Operations.BalanceInquiry]);
        }

        public void DoLogon()
        {
            Send(_models[Iso8583Operations.Logon]);
        }

        public String ResponseModel { get; private set; }

        public String Errors
        {
            get
            {
                var result = String.Empty;
                _errors.ForEach(ex => result += String.Format("[ERROR] {0}{1}", ex.Message, Environment.NewLine));
                return result;
            }
        }

        public bool HasErrors
        {
            get
            {
                return Errors != string.Empty;
            }
        }

        public StringBuilder DebugInfo { get; private set; }

        private bool IsRequestFinished { get; set; }

        private Object Response(Object model)
        {
            if (model == null)
            {
                Console.WriteLine("An error occured. Try to check/uncheck 'Use SSL/TLS' option.");
                _errors.Add(new Exception("No model info was received"));
            }
            else
            {
                //WriteLog("Print response bytes..");
                //Console.WriteLine(MessageUtility.HexToReadableString(ParsedMessage.AllBytes) + Environment.NewLine + Environment.NewLine);
                //WriteLog("End Print response bytes..");

                //WriteLog("Print response model..");
                ResponseModel = Util.GetReadableStringFromModel(model);
                Console.WriteLine(ResponseModel);
                //WriteLog("End Print model..");
            }
            IsRequestFinished = true;
            return null;
        }

        private void InitConfig(string configPath)
        {
            Stream fileConfig = null;
            try
            {
                fileConfig = MessageProcessor.ReadConfigFile(configPath);
                DebugInfo.AppendLine("[InitConfig] FILE READ: " + configPath);
            }
            catch (FileNotFoundException ex)
            {
                _errors.Add(ex);
                //Logger.GetInstance().Write(ex);
            }
            catch (Exception ex)
            {
                _errors.Add(ex);
            }
            if (fileConfig != null)
            {
                try
                {
                    DebugInfo.AppendLine("[InitConfig] BEFORE LOAD.");
                    _processor = new MessageProcessor();
                    _processor.Load(fileConfig);
                    DebugInfo.AppendLine("[InitConfig] LOADED.");
                    //WriteLog("Config loaded..");
                }
                catch (Exception ex)
                {
                    _errors.Add(ex);
                }
            }
        }

        private void InitModels()
        {
            _models = new Dictionary<Iso8583Operations, BaseModel>() {
                { Iso8583Operations.TransferInquiry, new ReqTransferInquiry0100() {
                        PrimaryAccountNumber = null,
                        ProcessingCode = new byte[] { 0x39, 0x10, 0x00 },
                        TransactionAmount = 7250000.00m,
                        SystemAuditTraceNumber = new byte[] { 0x00, 0x31, 0x07 },
                        ExpirationDate = null,
                        PosEntryMode = new byte[] { 0x00, 0x21 },
                        NetworkInternationalId = "005",
                        PosConditionCode = new byte[] { 0x00 },
                        Track2Data = new byte[] { 0x49, 0x91, 0x87, 0x02, 0x73, 0x00, 0x27, 0x3C,
                            0xD6, 0x2B, 0x27, 0x1A, 0x0A, 0x38, 0x08, 0x00, 0x80, 0x12, 0x40 },
                        TerminalId = "00001150",
                        MerchantId = "020001738000000",
                        AdditionalData = null,
                        CardholderPinBlock = new byte[] { 0x77, 0xBB, 0xAA, 0x66, 0x78, 0x3B, 0xD7, 0xCC },
                        InvoiceNumber = "003107",
                        TransferData = new Bit63Content {
                            TableId = "78",
		                    BeneficiaryInstitutionId = "00000002314",
		                    BeneficiaryAccountNumber = "0123456789012345",
		                    BeneficiaryName = null,
		                    CustomerReferenceNumber = null,
		                    IssuerInstitutionId = null,
		                    CardholderAccountNumber = null,
		                    CardholderName = null,
                            InformationData = null
                        },
                        MessageAuthenticationCode = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00 }
                    }
                },
                { Iso8583Operations.Transfer, new ReqTransfer0200() {
                        PrimaryAccountNumber = null,
                        ProcessingCode = new byte[] { 0x40, 0x10, 0x00 },
                        TransactionAmount = 7250000.00m,
                        SystemAuditTraceNumber = new byte[] { 0x00, 0x31, 0x07 },
                        ExpirationDate = null,
                        PosEntryMode = new byte[] { 0x00, 0x21 },
                        NetworkInternationalId = "005",
                        PosConditionCode = new byte[] { 0x00 },
                        Track2Data = new byte[] { 0x49, 0x91, 0x87, 0x02, 0x73, 0x00, 0x27, 0x3C,
                            0xD6, 0x2B, 0x27, 0x1A, 0x0A, 0x38, 0x08, 0x00, 0x80, 0x12, 0x40 },
                        TerminalId = "00001150",
                        MerchantId = "020001738000000",
                        AdditionalData = null,
                        CardholderPinBlock = new byte[] { 0x77, 0xBB, 0xAA, 0x66, 0x78, 0x3B, 0xD7, 0xCC },
                        InvoiceNumber = "003107",
                        TransferData = new Bit63Content {
                            TableId = "78",
		                    BeneficiaryInstitutionId = "00000002314",
		                    BeneficiaryAccountNumber = "0123456789012345",
		                    BeneficiaryName = null,
		                    CustomerReferenceNumber = null,
		                    IssuerInstitutionId = null,
		                    CardholderAccountNumber = null,
		                    CardholderName = null,
                            InformationData = null
                        },
                        MessageAuthenticationCode = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00 }
                    }
                },
                { Iso8583Operations.BalanceInquiry, new ReqBalanceInquiry0100() {
                        PrimaryAccountNumber = null,
                        ProcessingCode = new byte[] { 0x31, 0x10, 0x00 },
                        TransactionAmount = null,
                        SystemAuditTraceNumber = new byte[] { 0x00, 0x31, 0x07 },
                        ExpirationDate = null,
                        PosEntryMode = new byte[] { 0x00, 0x21 },
                        NetworkInternationalId = "005",
                        PosConditionCode = (byte)0x00,
                        Track2Data = new byte[] { 0x49, 0x91, 0x87, 0x02, 0x73, 0x00, 0x27, 0x3C,
                            0xD6, 0x2B, 0x27, 0x1A, 0x0A, 0x38, 0x08, 0x00, 0x80, 0x12, 0x40 },
                        TerminalId = "00001150",
                        MerchantId = "020001738000000",
                        AdditionalData = null,
                        CardholderPinBlock = new byte[] { 0x77, 0xBB, 0xAA, 0x66, 0x78, 0x3B, 0xD7, 0xCC },
                        InvoiceNumber = "003107",
                        TransferData = null,
                        MessageAuthenticationCode = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00 }
                    }
                },
                { Iso8583Operations.Logon, new ReqLogon0800() {
                        ProcessingCode = new byte[] { 0x92, 0x00, 0x00 },
                        SystemAuditTraceNumber = new byte[] { 0x00, 0x31, 0x07 },
                        NetworkInternationalId = "005",
                        TerminalId = "00001150",
                    }
                }
            };
        }

        private void Send(BaseModel model)
        {
            try
            {
                DebugInfo.Clear();
                IsRequestFinished = false;
                //TODO: Store IPAddress, Port and IsSslEnabled in config file
                var client = new MessageClient("200.91.79.23", 443, model, Response, _processor);
                client.IsSslEnabled = true;
                /*if (_client.IsSslEnabled)
                {
                    _client.ClientCertificates = _certificates;
                    _client.CertificateSelectionCallback = this.LocalCertificateSelectionCallback;
                    _client.CertificateValidationCallback = this.RemoteCertificateValidationCallback;
                }*/
                var msg = client.SendModel();
                while (!IsRequestFinished);
                DebugInfo.AppendLine("[CONNECTED] " + client.SuccessfulConnection.ToString());
                DebugInfo.AppendLine("[RECEIVE MODEL] " + Util.GetReadableStringFromModel(msg.ReceivedModel));
                DebugInfo.AppendLine("[RECEIVE MESSAGE] " + MessageUtility.HexToReadableString(msg.ReceivedMessage));
            }
            catch (Exception ex)
            {
                _errors.Add(ex);
                Console.WriteLine("Cannot connect to the server." + Environment.NewLine + ex.Message);
            }
            finally
            {
            }
        }        
    }

    internal enum Iso8583Operations
    {
        TransferInquiry,
        Transfer,
        BalanceInquiry,
        Logon
    }
}
